#include "environment.h"
#include <gl/glew.h>
#include <gl/glu.h>
#include <gl/glut.h>
#include "scene.h"

namespace Environment
{
	GLuint cubemap;
	const int size = 256;
	vector3		center;
	float		nearplane, farplane;
	int lastface = 0;
	void setup_face(int face)
	{
		switch (GL_TEXTURE_CUBE_MAP_POSITIVE_X+face)
		{
		case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
			glRotatef(180.0f,.0f,.0f,1.0f);
			glRotatef(90.0f,.0f,1.0f,.0f);
			break;
		case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
			glRotatef(180.0f,.0f,.0f,1.0f);
			glRotatef(-90.0f,.0f,1.0f,.0f);
			break;
		case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
			glRotatef(-90.0f,1.0f,.0f,.0f);
			break;
		case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
			glRotatef(90.0f,1.0f,.0f,.0f);
			break;
		case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
			glRotatef(180.0f,.0f,.0f,1.0f);
			glRotatef(180.0f,.0f,1.0f,.0f);
			break;
		case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
			glRotatef(180.0f,.0f,.0f,1.0f);
			break;
		}
	}
	void init(vector3 _center, float _near, float _far)
	{
		center = _center;
		nearplane = _near;
		farplane = _far;
		glGenTextures(1, &cubemap);
		glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap);
		glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
		for (int i = 0; i < 6; ++i)
			glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
	}
	void done()
	{
		glDeleteTextures(1, &cubemap);
	}
	void update_face(int face, void (*render_scene)())
	{
		glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity();
		gluPerspective(90.0, 1.0, nearplane, farplane);
		
		glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity();
		gluLookAt(center.x, center.y, center.z, center.x, center.y, center.z-10.0, 0.0, 1.0, 0.0);

		setup_face(face);

		int viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport);
		glViewport(0,0,size,size);

		render_scene();
		glCopyTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+face, 0, GL_RGBA, 0, 0, size, size, 0);

		glMatrixMode(GL_PROJECTION); glPopMatrix();
		glMatrixMode(GL_MODELVIEW); glPopMatrix();
		glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
	}

	void update_next_face(void (*render_scene)())
	{
		update_face(lastface, render_scene);
		if (++lastface==6)
			lastface = 0;
	}
	void begin()
	{
		glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
		glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
		glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
		glEnable(GL_TEXTURE_GEN_S);
		glEnable(GL_TEXTURE_GEN_T);
		glEnable(GL_TEXTURE_GEN_R);

		matrix44 view; glGetFloatv(GL_MODELVIEW_MATRIX, (float*)(&view[0][0]));
		view.transpose();

		glMatrixMode(GL_TEXTURE);
		glPushMatrix();
		glLoadMatrixf((float*)(&view));
		
		glEnable(GL_TEXTURE_CUBE_MAP);
		glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap);
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

		glMatrixMode(GL_MODELVIEW);
		// Set texture coordinate generation to GL_REFLECTION_MAP for S, T and R coordinates
		// Set texture transform matrix to transform vectors from camera space to scene space
		// Bind and enable cube map texture
	}
	void end()
	{
		
		glDisable(GL_TEXTURE_GEN_S);
		glDisable(GL_TEXTURE_GEN_T);
		glDisable(GL_TEXTURE_GEN_R);
		//glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
		glDisable(GL_TEXTURE_CUBE_MAP);
		glMatrixMode(GL_TEXTURE);
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
		
		// Disable cube map texture
		// Disable texture coordinate generation
		// Restore any state of OpenGL that might have been changed in begin()
	}
	void debug()
	{
		vector4 v[4];
		v[0] = vector4(1,0,0,0);
		v[1] = vector4(0,1,0,0);
		v[2] = vector4(0,0,1,0);
		v[3] = vector4(0,0,0,1);		
		glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGenfv(GL_S, GL_OBJECT_PLANE, (float*)&(v[0]));
		glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGenfv(GL_T, GL_OBJECT_PLANE, (float*)&(v[1]));
		glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGenfv(GL_R, GL_OBJECT_PLANE, (float*)&(v[2]));
		glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGenfv(GL_Q, GL_OBJECT_PLANE, (float*)&(v[3]));
		glEnable(GL_TEXTURE_GEN_S);
		glEnable(GL_TEXTURE_GEN_T);
		glEnable(GL_TEXTURE_GEN_R);
		glEnable(GL_TEXTURE_GEN_Q);
		glEnable(GL_TEXTURE_CUBE_MAP);
		glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap);
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
		glMatrixMode(GL_TEXTURE);
		glPushMatrix();
		glLoadIdentity();
		glMatrixMode(GL_MODELVIEW);

		glPushMatrix();
		glTranslatef(center.x, center.y, center.z);
		glScalef(2*nearplane,2*nearplane,2*nearplane);
		glutSolidCube(1.0);
		glPopMatrix();

		glDisable(GL_TEXTURE_GEN_S);
		glDisable(GL_TEXTURE_GEN_T);
		glDisable(GL_TEXTURE_GEN_R);
		glDisable(GL_TEXTURE_CUBE_MAP);
		glMatrixMode(GL_TEXTURE);
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);

	}

	void debug2()
	{
		begin();
		glPushMatrix();
		glTranslatef(center.x, center.y, center.z);
		glScalef(nearplane,nearplane,nearplane);
		glutSolidSphere(1.0,16,16);
		glPopMatrix();
		end();
	}
}